package algo;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;

import com.vividsolutions.jts.geom.Coordinate;

/**
 * 
 * @author Vaida
 */
public class Simplification {
	private Coordinate[] routeCoords; // original route
	private ArrayList<Integer> pointsToKeep;

	/**
	 * Read the file line by line
	 * 
	 * @param fileUrl
	 * @param algorithm
	 *            (0, 1, 2)
	 * @param epsilon
	 * @return indices of lines of the file after simplification (no first and
	 *         last point)
	 * 
	 */
	public Integer[] getStartRoute(String fileUrl, int algorithm, double epsilon) {
		Integer[] resArr = null;
		try {
			FileInputStream fstream = new FileInputStream(fileUrl);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));

			String strLine;
			br.readLine();
			ArrayList<Coordinate> coordArr = new ArrayList<Coordinate>();

			while ((strLine = br.readLine()) != null) {
				// System.out.println(j + " " + strLine.length() + " " +
				// strLine);
				String[] arr = strLine.split(" ");
				// System.out.println(arr[6] + " " + arr[7]);

				Coordinate coord = new Coordinate(Double.parseDouble(arr[6]),
						Double.parseDouble(arr[7]));
				coordArr.add(coord);
			}
			in.close();
			routeCoords = new Coordinate[coordArr.size()];
			coordArr.toArray(routeCoords);

			pointsToKeep = new ArrayList();
			// simplifyDouglasPeuckerFunctionStar(0, fixes.size() - 1);
			if (algorithm == 0)
				simplifyDouglasPeuckerFunction(0, routeCoords.length - 1,
						epsilon);
			System.out.println("rest points " + pointsToKeep.size());
			resArr = new Integer[pointsToKeep.size()];
			pointsToKeep.toArray(resArr);
			Arrays.sort(resArr);

			// for (int i = 0; i < resArr.length; i++) {
			// System.out.println(resArr[i]);
			// }
		} catch (Exception e) {
			System.err.println("Error: " + e.getMessage());
		}
		return resArr;
	}

	/**
	 * 
	 * @param p1x
	 * @param p1y
	 * @param p2x
	 * @param p2y
	 * @param px
	 * @param py
	 * @return
	 */
	private double distanceToSegment(double p1x, double p1y, double p2x,
			double p2y, double px, double py) {
		double area = Math.abs(0.5 * (p1x * p2y + p2x * py + px * p1y - p2x
				* p1y - px * p2y - p1x * py));
		double bottom = Math.sqrt(Math.pow(p1x - p2x, 2)
				+ Math.pow(p1y - p2y, 2));
		double height = area / bottom * 2;

		return height;
	}

	/**
	 * TODO: support other variant of DP algorithm
	 * 
	 * @param algorithm
	 * @param epsilon
	 * @return array of indices including the first and last point
	 */
	public Integer[] simplifyFromArr(int algorithm, double epsilon) {
		Integer[] resArr = null;
		pointsToKeep = new ArrayList();

		if (algorithm == 0) {
			simplifyDouglasPeuckerFunction(0, routeCoords.length - 1, epsilon);
		}
		// pointsToKeep.add(0, 0);

		System.out
				.println("after sim, remaining points " + pointsToKeep.size());
		resArr = new Integer[pointsToKeep.size()];
		pointsToKeep.toArray(resArr);

		Arrays.sort(resArr);
		return resArr;
	}

	/**
	 * xiaohui: add the first and last points into result
	 * 
	 * @param algorithm
	 * @param epsilon
	 * @return simplified trajectory
	 */
	public Integer[] simplifyArr(int algorithm, double epsilon) {
		Integer[] resArr = null;
		pointsToKeep = new ArrayList();

		if (algorithm == 0) {
			simplifyDouglasPeuckerFunction(0, routeCoords.length - 1, epsilon);
		}
		// pointsToKeep.add(0, 0);

		System.out
				.println("after sim, remaining points " + pointsToKeep.size());
		
		// add first and last points
		pointsToKeep.add(0);
		pointsToKeep.add(routeCoords.length - 1);

		resArr = new Integer[pointsToKeep.size()];
		pointsToKeep.toArray(resArr);

		Arrays.sort(resArr);
		return resArr;
	}

	/**
	 * 
	 * @param start
	 * @param end
	 * @param epsilon
	 */
	private void simplifyDouglasPeuckerFunction(int start, int end,
			double epsilon) {

		double maxDistance = 0.0;
		int farthestIndex = 0;

		for (int i = start; i < end; i++) {
			double distance = distanceToSegment(routeCoords[start].x,
					routeCoords[start].y, routeCoords[end].x,
					routeCoords[end].y, routeCoords[i].x, routeCoords[i].y);
			if (distance > maxDistance) {
				maxDistance = distance;
				farthestIndex = i;
			}
		}
		// System.out.println("tolimiausias " + farthestIndex + " dist=" +
		// maxDistance);
		if (maxDistance > epsilon && farthestIndex != 0) {
			pointsToKeep.add(farthestIndex);
			// distances.put(farthestIndex, maxDistance);
			simplifyDouglasPeuckerFunction(start, farthestIndex, epsilon);
			simplifyDouglasPeuckerFunction(farthestIndex, end, epsilon);
		}
	}

	public void setRouteCoords(Coordinate[] routeCoords) {
		this.routeCoords = routeCoords;
	}

	public Coordinate[] getRouteCoords() {
		return routeCoords;
	}
}
